﻿/*  ----------------------------------------------------------------------------
 *  DES
 *  ----------------------------------------------------------------------------
 *  File:       ResultForwarder.cs
 *  Author:     nitinkoshy@gmail.com
 *  ----------------------------------------------------------------------------
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ServiceModel;
using System.Configuration;
using System.Messaging;
using System.Diagnostics;

namespace DES.Server.Service
{
    public class ResultForwarder
    {
        private Thread _visitorThread = new Thread(CheckQueue);
        private static bool _stopped = false;
        private static int _threadSleepTime;


        public ResultForwarder()
        {
            _threadSleepTime = Convert.ToInt16(ConfigurationManager.AppSettings["forwarderthreadsleeptime"]);
        }

        public static void CheckQueue(object data)
        {
            DESServerService serverService = (DESServerService) data;
            ServerTask serverTask;
            while (!_stopped)
            {
                Thread.Sleep(_threadSleepTime); 

                if (serverService.ProcessedTasks.Count == 0)
                    continue;

                Trace.WriteLine(String.Format("ResultForwarder Message : Processed queue count : {0}", serverService.ProcessedTasks.Count));
                lock (serverService.ProcessedTasks)
                {
                    serverTask = serverService.ProcessedTasks.Dequeue();
                }

                //serverTask.MarkAsReceived();

                //first check if I am the root/root
                if ((serverTask.RootServer == null) || (serverTask.RootServer.Id == serverService.ServerInfo.Id))
                {
                    Trace.WriteLine(String.Format("ResultForwarder Message : I am the root for task : {0}", serverTask.Task.ShortId));
                    //client has a endpoint?
                    if (serverTask.Task.EndPoint != String.Empty)
                    {
                        //for the moment the endpoint is assumed to be a MSMQ queue
                        //tomorrow it could be a SOAP/WCF/REST endpoint etc
                        try
                        {
                            Trace.WriteLine(String.Format("ResultForwarder Message : Storing result to msmq : {0}", serverTask.Task.ShortId));
                            MessageQueue msgQ = new MessageQueue(serverTask.Task.EndPoint);
                            Message msg = new Message();

                            if (serverTask.Task.GeneratedError)
                            {
                                msg.Body = serverTask.Task.ExecutionError;
                                msg.Priority = MessagePriority.AboveNormal;
                            }
                            else
                                msg.Body = serverTask.Task.TaskResult;

                            msgQ.Send(msg, serverTask.Task.Id.ToString());
                        }
                        catch (Exception E)
                        {
                            Trace.WriteLine(String.Format("ResultForwarder Message : Error while storing : {0}", serverTask.Task.ShortId));
                        }
                    }
                    continue;
                }

                Trace.WriteLine(String.Format("ResultForwarder Message : Trying to move the task {0} up the call tree", serverTask.Task.ShortId));
                //check if the task root is directly accessible from here
                DESServerInfo nextServer = null;
                DESServerInfo rootServer = IsDirectlyConnectedToServer(serverTask.RootServer.Id, serverService);
                if (rootServer != null)
                {
                    nextServer = rootServer;
                    Trace.WriteLine(String.Format("ResultForwarder Message : Root server {0} for task {1} directly connected", nextServer.Id, serverTask.Task.ShortId));
                }
                else
                {
                    nextServer = serverTask.ImmediateParentServer;
                    Trace.WriteLine(String.Format("ResultForwarder Message : Root server NOT directly connected"));
                    Trace.WriteLine(String.Format("ResultForwarder Message : Moving up the call tree to {0}", nextServer.Id));
                }

                if (nextServer == null)
                    throw new ApplicationException("Next Server Empty while trying to forward");

                ChannelFactory<IDESServerService> desServerServiceFactory = new ChannelFactory<IDESServerService>(
                            new NetTcpBinding(),
                            new EndpointAddress(nextServer.EndPoint));
                IDESServerService desServerService = desServerServiceFactory.CreateChannel();


                try
                {
                    Trace.WriteLine(String.Format("ResultForwarder Message : calling the next parent for task {0}", serverTask.Task.ShortId));
                    desServerService.SetProcessedTask(serverTask, serverService.ServerInfo);
                    desServerServiceFactory.Close();
                }
                catch (EndpointNotFoundException exception)//this server is not connected
                {
                    lock (serverService.ProcessedTasks)
                    {
                        Trace.WriteLine(String.Format("ResultForwarder Message : could not move task {0} up the tree, enqueue back", serverTask.Task.ShortId));
                        serverService.ProcessedTasks.Enqueue(serverTask); //put the task back
                    }
                    continue;
                }
            }
        }

        public void StartMonitoring(DESServerService serverService)
        {
           _visitorThread.Start(serverService);
           Trace.WriteLine(String.Format("ResultForwarder thread started with sleep time {0}", _threadSleepTime.ToString()));
        }

        public void StopMonitoring()
        {
            _stopped = true;
        }

        static DESServerInfo IsDirectlyConnectedToServer(string serverId, DESServerService serverService)
        {
            foreach (DESServerInfo server in serverService.ConnectedServers)
                if (server.Id == serverId)
                    return server;

            return null;
        }
    }
}
